package com.leyou.Search.service;

import com.leyou.Search.clients.BrandClient;
import com.leyou.Search.clients.CategoryClient;
import com.leyou.Search.clients.GoodsClient;
import com.leyou.Search.clients.SpecClient;
import com.leyou.Search.pojo.Goods;
import com.leyou.Search.repository.GoodsRepository;
import com.leyou.Search.utils.SearchRequest;
import com.leyou.Search.utils.SearchResult;
import item.pojo.Brand;
import item.pojo.Category;
import item.pojo.SpecParam;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.LongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.FetchSourceFilter;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


/**
 * @Author: taft
 * @Date: 2018-11-22 12:30
 */
@Service
public class SearchService {

    @Autowired
    private GoodsRepository goodsRepository;

    @Autowired
    private CategoryClient categoryClient;

    @Autowired
    private BrandClient brandClient;

    @Autowired
    private SpecClient specClient;

    @Autowired
    private GoodsClient goodsClient;

    public SearchResult pageQuery(SearchRequest searchRequest) {

        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();


        queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{"id","skus","subTitle"}, null));

        //封装查询条件
        QueryBuilder query  = buildBasicQueryWithFilter(searchRequest);

        //把查询条件加入查询构架器
        queryBuilder.withQuery(query);

        //spring Data 中所有的分页都从0开始
        queryBuilder.withPageable(PageRequest.of(searchRequest.getPage()-1,searchRequest.getSize()));

        String categoryAggName = "category"; // 商品分类聚合名称
        String brandAggName = "brand"; // 品牌聚合名称

        queryBuilder.addAggregation(AggregationBuilders.terms(categoryAggName).field("cid3"));
        // 对品牌进行聚合
        queryBuilder.addAggregation(AggregationBuilders.terms(brandAggName).field("brandId"));

        AggregatedPage<Goods> search = (AggregatedPage<Goods>) goodsRepository.search(queryBuilder.build());

        LongTerms categoryTerms=(LongTerms) search.getAggregation(categoryAggName);

        List<LongTerms.Bucket> categoryTermsBuckets = categoryTerms.getBuckets();

        //聚合后所有的分类的id
        List<Long> cids=new ArrayList<>();

        //聚合后所有的分类的id
        for (LongTerms.Bucket categoryTermsBucket : categoryTermsBuckets) {
            Long cid = categoryTermsBucket.getKeyAsNumber().longValue();

            cids.add(cid);
        }

        LongTerms brandTerms=(LongTerms)search.getAggregation(brandAggName);

        List<LongTerms.Bucket> brandTermsBuckets = brandTerms.getBuckets();

        //聚合后所有的品牌的id
        List<Long> brandIds = new ArrayList<>();
        for (LongTerms.Bucket brandTermsBucket : brandTermsBuckets) {
            brandIds.add(brandTermsBucket.getKeyAsNumber().longValue());
        }

        List<Category>  categories = new ArrayList<>();

        //根据分类的id查询分类对应的名字
        List<String> names = this.categoryClient.queryNameByIds(cids);


        //根据分类的id以及名字封装分类的对象结果
        for (int i = 0; i < names.size(); i++) {
            Category category = new Category();
            category.setId(cids.get(i));
            category.setName(names.get(i));
            categories.add(category);
        }

        List<Brand> brands = new ArrayList<>();

        brandIds.forEach(brandId->brands.add(brandClient.queryBrandById(brandId)));

        List<Map<String,Object>> specs=null;

        if(categories.size()==1){
            specs=getSpecs(categories.get(0).getId(),query);
        }
        return new SearchResult(search.getTotalElements(), new Long(search.getTotalPages()), search.getContent(), categories, brands, specs);
    }

    private QueryBuilder buildBasicQueryWithFilter(SearchRequest searchRequest) {

        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

        // 基本查询条件
        boolQueryBuilder.must(QueryBuilders.matchQuery("all",searchRequest.getKey()).operator(Operator.AND));
       // 过滤条件构建器
        BoolQueryBuilder filterQueryBuilder = QueryBuilders.boolQuery();

        Map<String, String> filter = searchRequest.getFilter();

        for(Map.Entry<String,String> entry : filter.entrySet()){
            String key = entry.getKey();
            String value = entry.getValue();
            if(key!="cid3"&&key!="brandId"){
                key="specs."+key+".keyword";
            }
            filterQueryBuilder.must(QueryBuilders.termQuery(key,value));
        }
        boolQueryBuilder.filter(filterQueryBuilder);

        return boolQueryBuilder;
    }

    private List<Map<String,Object>> getSpecs(Long cid, QueryBuilder query){

        List<Map<String,Object>> specs=new ArrayList<>();

        //所有的需要被聚合的规格参数集合
        List<SpecParam> specParams = specClient.querySpecParam(null, cid, true, null);

        NativeSearchQueryBuilder queryBuilder=new NativeSearchQueryBuilder();

        queryBuilder.withQuery(query);

        specParams.forEach(specParam -> {
            //就是规格属性的名字，例如。CPU品牌，CPU频率
            String name=specParam.getName();
            queryBuilder.addAggregation(AggregationBuilders.terms(name).field("specs."+name+".keyword"));
        });
        // 查询
        AggregatedPage<Goods> aggregatedPage=(AggregatedPage<Goods>) goodsRepository.search(queryBuilder.build());

        Map<String, Aggregation> aggregationMap = aggregatedPage.getAggregations().asMap();

        specParams.forEach(specParam -> {
            String name=specParam.getName();

            StringTerms aggregation=(StringTerms) aggregationMap.get(name);

            List<String> paramResults = new ArrayList<>();
            List<StringTerms.Bucket> buckets = aggregation.getBuckets();

            for (StringTerms.Bucket bucket : buckets) {
                paramResults.add(bucket.getKeyAsString());
            }

            Map<String,Object> spec = new HashMap<>();
            spec.put("k",name);
            spec.put("options",paramResults);

            specs.add(spec);
        });

        return specs;
    }
}
